import numpy as np
import pandas as pd
import statsmodels.api as sm
import matplotlib.pyplot as plt
pd.set_option("display.max_columns",500)
#df[index],这是取一列
#df.loc[index]这是取一行
#使用df.loc[]进行切片，切片以后的矩阵的行标和原来的保持一致，列标重新排列(从1开始)
#但是：对行进行索引的时候要按照行标来，对列进行索引时要从0开始
#pandas里面通过=赋值，是浅拷贝，应该用DataFrame.copy(deep=True)实现深拷贝
#.loc和iloc只输入一维时选取的是行，而[]选取的是列，并且必须使用列名
#iloc是用来按照行标、列表进行索引的。但是iloc是左闭右开,比如0:5,只能去0,1,2,3,4，取不到5
#pandas里面的NaN可以显示指定，其表达式诶np.nan,如df.loc[1,1]=np.nan
#t1=df.apply(lambda x:((x-x.mean())/x.std(ddof=0)))对df的每一列进行标准化
#在Pandas中，DataFrame的一列就是一个Series, 可以通过map来对一列进行操作：df['col2'] = df['col1'].map(lambda x: x**2)
#其中lambda函数中的x代表当前元素
#要对DataFrame的多个列同时进行运算，可以使用apply，例如col3 = col1 + 2 * col2:
# df['col3'] = df.apply(lambda x: x['col1'] + 2 * x['col2'], axis=1)
# 其中x带表当前行，可以通过下标进行索引
#numpy有四种乘法，*代表点对点相乘，ndarray1.dot(ndarray2)代表矩阵乘法，
#在pandas中，dot()不是matlab中的.*,假设df1为1行5列，df2为3行5列，那么df1*df2只有第一行有数据，其他权威NaN，但是如果两个维度一样，那么就是点积
# ，pandas貌似不能直接支持.*,但是每个df都有一个to_numpy()方法或者.values属性，调用这个方法将pd.DataFrame转换成ndarray
#ndarray的*就是点积
#df.idxmax()求一列的最大值的行标，如df[1].idxmax()求的是第二列的最大值的行标
#df.drop([标号列表],axis=1)axis=1删除列，=0删除行，ignore_index默认为false，就不会重新编号
#选取指定列值最大的那些行：df.nlargest(n=10,columns='colName')
#2个df，即使维度一样，但是只要index或者column的标号不同，也不能直接相乘，否则就会出现NaN，可以转为ndarray或者reindex
# 总结来说，groupby的过程就是将原有的DataFrame按照groupby的字段（这里是company），
# 划分为若干个分组DataFrame，被分为多少个组就有多少个分组DataFrame。
# 所以说，在groupby之后的一系列操作（如agg、apply等），均是基于子DataFrame的操作。

path = 'D:\\ProgramData\\MATLABProjects\\2020data01.xlsx'

#hp滤波剔除极值
def hpFilter(path) ->pd.DataFrame:
    initExcel = pd.read_excel(path,sheet_name='initial', header=None)
    # 数值部分：[5:,1:]
    initData = initExcel.loc[5:, 1:]  # initData的行标是和initExcel一样的，但是列标重新从1到76编号
    hpT = pd.DataFrame()  # 用来装载hp滤波后的dataframe
    notNa = ~initData.isna()
    for col in range(1, initData.shape[1] + 1):
        cycle, trend = sm.tsa.filters.hpfilter(initData.loc[notNa.loc[:, col], col],lamb=1600)
        hpT = pd.concat([hpT, trend], axis=1)
    hpT.to_excel('D:\\ans.xlsx')
    subMat = initData - hpT  # 残差矩阵
    stdMat = subMat.std(axis=0, skipna=True, ddof=0)  # 对每列的所有行求标准差，跳过NaN，并且求的是总体标准差而不是样本标准差，skipna默认为True，不指定也行
    for col2 in range(1, subMat.shape[1] + 1):
        subMat.loc[(abs(subMat.loc[:, col2]) > 1.5 * stdMat[col2]) & (subMat.loc[:, col2] < 0), col2] = -1.5 * abs(
            stdMat[col2])
        subMat.loc[(abs(subMat.loc[:, col2]) > 1.5 * stdMat[col2]) & (subMat.loc[:, col2] > 0), col2] = 1.5 * abs(
            stdMat[col2])
    ansMat = hpT + subMat
    initExcel.loc[5:,1:]=ansMat
    #交换第0行和第4行位置，让X1,X2,X3...这一行变成第一行
    a,b=initExcel.loc[0,:].copy(deep=True),initExcel.loc[4,:].copy(deep=True)
    initExcel.loc[0,:],initExcel.loc[4,:]=b,a
    #处理日期格式，不然写入excel后日期就变成了#######
    initExcel.loc[5:,0]=initExcel.loc[5:,0].apply(lambda x: str(pd.to_datetime(x).date()))
    #指定header=None，Index=False，不然会将行、列的索引号也写入excel中
    initExcel.to_excel(excel_writer='D:\\python跑出来hp滤波.xlsx',sheet_name='Sheet1',header=None,index=False)
    print(initExcel)
    return initExcel

#cross_cor
# 测试代码：
# x=pd.DataFrame(np.array([3.98,8.23,9.9,3.3,2.95,21.43,17.64,8.222]))
# y=pd.DataFrame(np.array([1.38,2.90,4.34,3.64,5.89,7.56,8.23,9.53]))
# L=6
# ansT=cross_cor(x,y,L)
# print(ansT)
#经过和matlab的多组不同数据结果对比，该函数正确
def cross_cor(x,y,L)->pd.DataFrame:#x,y都是Series
    T=y.shape[0]
    p=pd.DataFrame(np.zeros((1,2*L+1),dtype=np.float))#1行，2L+1列
    varx=(x-x.mean())*(x-x.mean())
    # sigma_x=varx.sum().apply(lambda x:x**0.5)[0]#sum()返回的是一个1*1的df，apply后还是1*1
    sigma_x= np.sqrt(varx.sum())#sum()返回的是一个1*1的df，apply后还是1*1
    vary=(y-y.mean())*(y-y.mean())
    # sigma_y = vary.sum().apply(lambda y: y ** 0.5)[0]
    sigma_y = np.sqrt(vary.sum())
    print(sigma_x)
    print(sigma_y)
    j=0
    for i in range(-L,0):#取不到0
        n1=T+i
        n2=-i
        u=(y.iloc[0:n1,:]-y.mean())
        u=u.values
        v=x.iloc[n2:,:]-x.mean()
        v=v.values
        uv=u*v
        # m1=((y.iloc[0:n1,:]-y.mean())*(x.iloc[n2:,:]-x.mean()))#这样写不行，因为(y.iloc[0:n1,:]-y.mean())和x.iloc[n2:,:]-x.mean()的index不同，*会变成Nan
        s1=uv.sum(axis=0)
        # ss1=s1[0]
        p[j]=pd.DataFrame([s1[0]/(sigma_y*sigma_x)])
        # print(p[j])
        j+=1
    for i in range(0,L+1):
        n1=T-i
        n2=i
        e=(x.iloc[0:n1,:]-x.mean())
        e = e.values
        r=(y.iloc[n2:,:]-y.mean())
        r=r.values
        er=e*r
        s2=er.sum(axis=0)
        # print(s2[0])
        p[j]=pd.DataFrame([s2[0]/(sigma_x*sigma_y)])
        # print(p[j])
        j+=1
    period=pd.DataFrame(np.arange(L,-L-1,-1)).T
    p=pd.concat([period,p],axis=0,ignore_index=True)#纵向拼接

    p=p.T
    p=pd.concat([p,p[1].abs()],axis=1,ignore_index=True)#增加一列abs值，方便查找至最大的那一列，待会不取就完事
    #现在的p第0列是期数，第1列是对应的相关系数，第2列是第1列的绝对值，要找第2列的最大值的索引
    print(p)
    ##原来的
    # p=p.values
    # idx=p[:,2].argmax(axis=0)
    # print(idx)
    # ansT=p[idx,[1,0]]
    # ansDf=pd.DataFrame(ansT)
    # ansDf=ansDf.T
    # # print(ansDf)
    # return ansDf
    ##原来的结束分割线

    #新的
    p=p.fillna(value=np.NINF)
    p = p.values
    idx = p[:, 2].argmax(axis=0)
    print(idx)
    ansT = p[idx, [1, 0]]
    ansDf = pd.DataFrame(ansT)
    ansDf = ansDf.T
    # print(ansDf)
    return ansDf
    # if (p.isnull().sum()==0)&(p.shape[0]>0):
    #     p=p.values
    #     idx=p[:,2].argmax(axis=0)
    #     print(idx)
    #     ansT=p[idx,[1,0]]
    #     ansDf=pd.DataFrame(ansT)
    #     ansDf=ansDf.T
    #     # print(ansDf)
    #     return ansDf
    # else:
    #     idx=p[2].idxmax(axis=0)
    #     p=p.values
    #     goal=p[idx,[1,0]]
    #     ansDf=pd.DataFrame(goal)
    #     ansDf=ansDf.T
    #     # print(ansDf)
    #     return ansDf
    #新的结束
# x=pd.DataFrame(np.array([9.99, 1.312, 2.3 ,3.32 ,9.32,  2.0 , 4.0 , 19.0,  13.0 , 10.0]))
# print(x)
# y=pd.DataFrame(np.array([28,  25,  7 , 0,  24,  6 , 23 , 16,  3 , 3]))
# print(y)

def cor(x,y,L)->pd.DataFrame:#x,y都是Series
    T=y.shape[0]
    p=pd.DataFrame(np.zeros((1,2*L+1),dtype=np.float))#1行，2L+1列
    j=0
    for i in range(-L,0):#取不到0
        n1=T+i
        n2=-i
        goal1=y.iloc[0:n1,:]
        goal2=x.iloc[n2:,:]
        ansCor=goal1.corrwith(goal2)
        tmp=pd.DataFrame(np.array([ansCor]))
        p[j]=tmp.loc[0]
        j+=1
    for i in range(0,L+1):
        n1=T-i
        n2=i
        goalone=x.iloc[0:n1,:]
        goaltwo=y.iloc[n2:,:]
        goalCor=goaltwo.corrwith(goalone)
        tmp1=pd.DataFrame(np.array([goalCor]))
        p[j]=tmp1.loc[0]
        j+=1
    period=pd.DataFrame(np.arange(L,-L-1,-1)).T
    p=pd.concat([period,p],axis=0,ignore_index=True)#纵向拼接
    p=p.T
    p=pd.concat([p,p[1].abs()],axis=1,ignore_index=True)#增加一列abs值，方便查找至最大的那一列，待会不取就完事
    #现在的p第0列是期数，第1列是对应的相关系数，第2列是第1列的绝对值，要找第2列的最大值的索引
    print(p)
    p=p.values
    idx=p[:,2].argmax(axis=0)
    print(idx)
    ansT=p[idx,[1,0]]
    ansDf=pd.DataFrame(ansT)
    ansDf=ansDf.T
    return ansDf

#自定义标准化函数
def zscore(df_input):
    return df_input.apply((lambda x: (x-x.mean())/ x.std(ddof=0)))
#求解相关系数
def getCorre(path,L):
    excel=pd.read_excel(path,sheet_name='Sheet1',header=None)#header=None:代表不适用源表的表头
    numData=excel.iloc[5:,1:]
    numData=numData.reset_index(drop=True)#注意，现在的numData行索引从0开始，列索引从1开始，因为没有重置列索引
    print(type(numData))
    [h,k]=numData.shape
    rho=pd.DataFrame(np.zeros((k,2))*np.nan)
    for col in range(1,k+1):
        nx=numData.iloc[:,col].values
        ny=numData.iloc[:,0].values
        nx=(nx-np.mean(nx))/np.std(nx,axis=0)
        ny=(ny-np.mean(nx))/np.std(ny,axis=0)
        x=pd.DataFrame(nx)
        y=pd.DataFrame(ny)
        res=cor(x,y,L)
        # print(res)
        res=res.values
        res=pd.DataFrame(res).loc[0]
        rho.iloc[col,:]=res
    rho_sorted=rho.sort_values(by=[1,0],axis=0)
    flag=rho_sorted.index
    name=excel.iloc[0:5,1:].T
    name_sorted=name.loc[flag,:]
    ansDf=pd.concat([name_sorted,rho_sorted],axis=1,ignore_index=True)
    # print(ansDf)
    ansDf.to_excel(excel_writer='D:\\求解相关系数.xlsx',sheet_name='Sheet1',header=None,index=False)
    return ansDf
    #测试数据
    # path = 'D:\\pyhpfilter.xlsx'
    # L = 6
    # ansDf = getCorre(path, L)
    # print(ansDf)




if __name__ == '__main__':
    # path = 'D:\\pyhpfilter.xlsx'
    # L = 6
    # ansDf = getCorre(path, L)
    path='D:\\2020data01.xlsx'
    hpFilter(path)
